package sub2;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.NoResultException;
import javax.persistence.Persistence;

/**
 * Utility class for manipulating entities
 * @author Magali Moulin, Pierre Labiausse
 * @version 2
 */
public class Directory {
	private EntityManager em;
	
	/**
	 * Constructor of this class. Create an entity manager to manipulate entities stored in the database
	 */
	public Directory() {
		em = Persistence.createEntityManagerFactory("fish_directory").createEntityManager();
	}
	
	private EntityTransaction beginTransaction(){
		EntityTransaction transaction = em.getTransaction();
		transaction.begin();
		return transaction;
	}
	
	private void commitTransaction(EntityTransaction transaction) {
		transaction.commit();
	}
	
	/**
	 * Add a file in the directory or a peer to the file's providers
	 * @param filename: name of the file to add
	 * @param peer: name of the peer who shares the file
	 * @return fichier: the new file stored in the directory
	 */
	//Add a file in the directory
	public Fichier newFichier(String filename, Peer peer)  {
		EntityTransaction transaction = null;
		try {
			transaction = beginTransaction();
			Fichier fichier = null;
			// check if the file is already there
			try {
				fichier=(Fichier) em.createNamedQuery("findFichierWithFilename")
				.setParameter("filename",filename)
				.getSingleResult();
				// If the file is there, add the peer to the list of peers
				Set<Peer> _tmp = fichier.getPeers();
				_tmp.add(peer);
				fichier.setPeer(_tmp);
				peer.addFichier(fichier);
				return fichier;
			} catch (NoResultException e){
				// If the file is not already there create it
				Set<Peer> _tmp = new HashSet<Peer>();
				_tmp.add(peer);
				Fichier _fichier = new Fichier(_tmp,filename);
				peer.addFichier(_fichier);
				em.persist(_fichier);
				return _fichier;
			}
		} finally {
			commitTransaction(transaction);
		}
		
	}
	
	/**
	 * Add a peer in the directory
	 * @param inetAddress: inetAddress of the peer
	 * @param port: port of the peer
	 * @return peer: the new peer stored in the database
	 */
	//Add a peer in the directory
	public Peer newPeer(String inetAddress,int port)  {
		EntityTransaction transaction = null;
		try {
			transaction = beginTransaction();
			Peer peer = new Peer(inetAddress,port);
			em.persist(peer);
			return peer;
		} finally {
			commitTransaction(transaction);
		}
	}
	
	/**
	 * Search for files in the database
	 * @param filename: name of the file which must be loaded
	 * @return list : a list of Fichier which matches with the searched filename
	 */
	@SuppressWarnings("unchecked")
	public List<Fichier>  getFichier(String filename) {
		if (filename == null) {
			return null;
		}
		try {
			java.util.List<Fichier> _list=em.createNamedQuery("findFichierWithFilename").
			setParameter("filename", filename).getResultList();
			return _list;
		} catch (NoResultException noSuchFile) {
			return null;			
		}
	}
	
	/**
	 * Load a peer from the database
	 * @param inetAddress: inetAddress of the peer
	 * @param port: port of the peer
	 * @return peer: the loaded peer
	 */
	//Load an peer from the database
	public Peer getPeer(String inetAddress, int port) {
		if (inetAddress == null) {
			return null;
		}
		try {
			return (Peer) em.createNamedQuery("findPeerWithInetAddressAndPort").
					setParameter("inetAddress", inetAddress)
					.setParameter("port", ""+port)
					.getSingleResult();
		} catch (NoResultException noSuchFile) {
			return null;			
		}
	}
	
	/**
	 * Remove a peer from the directory
	 * @param inetAddress: the inetAddress of the peer who must be removed
	 * @param port: the port of the peer who must be removed
	 */
	//Remove a peer 
	public void deletePeer(String inetAddress, int port) {
		EntityTransaction transaction = null;
		try {
			transaction = beginTransaction();

			Peer peer = getPeer(inetAddress, port);
			if(peer!=null){
			System.out.println("peer:"+peer);
			// List files shared by the peer
			for(Fichier fichier : peer.getFichiers()){
				// If there was only one peer sharing, remove the file
				if(fichier.getPeers().size() <= 1){
					em.remove(fichier);
				}
				// Otherwise remove the peer from the peer list in the file
				else {
					Set<Peer> _tmp = fichier.getPeers();
					_tmp.remove(peer);
					fichier.setPeer(_tmp);
				}
				System.out.println(fichier.getFilename());
				fichier.getPeers().remove(peer);
			}
			//em.flush();
			//em.merge(peer);
			em.remove(peer);
			}
		} finally {
			commitTransaction(transaction);
		}
	} 

	/**
	 * List of the files in the directory
	 * @return result: the list of files 
	 */
	//List fichier
	public HashMap<String, ArrayList<ClientTcpTuple>> listFichier(){
		java.util.List<Fichier> _list = em.createNamedQuery("listFichiers", Fichier.class)
		.getResultList();
		HashMap<String, ArrayList<ClientTcpTuple>> result = new HashMap<String, ArrayList<ClientTcpTuple>>();
		for(Fichier fichier: _list){	
			System.out.println("Corresponding file : "+fichier);
			ArrayList<ClientTcpTuple> tmp = new ArrayList<ClientTcpTuple>();
			for(Peer peer: fichier.getPeers()){		
				String inetAddress=peer.getInetAddress();
				int port= peer.getPort();
				ClientTcpTuple tuple = null;
				try {
					tuple = new ClientTcpTuple(InetAddress.getByName(inetAddress), port);
				} catch (UnknownHostException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}			
				tmp.add(tuple);
			}
			result.put(fichier.getFilename(), tmp);
		}
		return result;
	}	

	/**
	 * List of registered peers in the directory
	 * @return result: the list of peers
	 */
	//List Peer
	public ArrayList<ClientTcpTuple>listPeer(){
		java.util.List<Peer> _list = em.createNamedQuery("listPeer", Peer.class)
		.getResultList();
		ArrayList<ClientTcpTuple> result = new  ArrayList<ClientTcpTuple>();
		for(Peer peer: _list){			
			String inetAddress=peer.getInetAddress();
			int port= peer.getPort();
			ClientTcpTuple tuple = null;
			try {
				tuple = new ClientTcpTuple(InetAddress.getByName(inetAddress), port);
			} catch (UnknownHostException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}			
			result.add(tuple);

		}
		return result;
	}
}
